package utils

import chisel3._
import chisel3.util._

class CircularQueuePtr[T <: CircularQueuePtr[T]](val entries: Int) extends Bundle {

  val ptrWidth = log2Up(entries)

  val flag = Bool()
  val value = UInt(ptrWidth.W)

  final def +(v: UInt): T = {
    val newPtr = Wire(this.asInstanceOf[T].cloneType)
    if(isPow2(entries)) {
      newPtr := (Cat(this.flag, this.value) + v).asTypeOf(newPtr)
    }
    newPtr
  }

  final def -(v: UInt): T = {
    val flipped_new_ptr = this + (this.entries.U - v)
    val newPtr = Wire(this.asInstanceOf[T].cloneType)
    newPtr.flag := !flipped_new_ptr.flag
    newPtr.value := flipped_new_ptr.value
    newPtr
  }
  final def === (that_ptr: T): Bool = {
    this.asUInt() === that_ptr.asUInt()
  }
  final def =/= (that_ptr: T): Bool = {
    this.asUInt() =/= that_ptr.asUInt()
  }
}


trait CircularQueuePtrHepler {

  def distancePtr[T <: CircularQueuePtr[T]](enq_ptr: T, deq_ptr: T): UInt = {
    assert(enq_ptr.entries == deq_ptr.entries)
    Mux(enq_ptr.flag === deq_ptr.flag, enq_ptr.value - deq_ptr.value,
      enq_ptr.entries.U + enq_ptr.value - deq_ptr.value)
  }
}


